﻿using System.Windows.Forms;
using System.Xml;
using System.Text;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Helper;
using SdmxMl.Manager;
using System.Collections.Generic;


namespace SdmxMl.GenericMetadata
{
    /// <summary>
    /// The Metadata Set is a set of reported metadata against a set of values for
    /// a given full or partial target identifier, as described in a metadata
    /// structure definition. Child elements include identification of the relevant
    /// metadata structure definition using the MetadataStructureRef and
    /// MetadataStructureAgencyRef elements. The ReportRef element includes the ID
    /// of the report structure as described in the metadata structure definition.
    /// AttributeValueSet is a repeatable child element which allows target identifier
    /// keys and their associated metadata attribute values to be reported
    /// (this functions like a series element does for data sets). An optional name
    /// and annotations may also be supplied. The metadataStructureURI allows for
    /// a URI to be provided, pointing to the SDMX-ML Structure Message representation
    /// of the referenced metadata structure definition. Attributes are provided for
    /// describing the contents of a data or metadata set, which are particularly
    /// important for interactions with the SDMX Registry: datasetID,
    /// dataProviderSchemeAgencyID, dataProviderSchemeID, dataflowAgencyID, and
    /// dataflowID all take the IDs specified by the attribute names.
    /// The action attribute indicates whether the file is appending, replacing,
    /// or deleting. Attributes reportingBeginDate, reportingEndDate, validFromDate,
    /// and validToDate are inclusive. publicationYear holds the ISO 8601 four-digit
    /// year, and publicationPeriod specifies the period of publication of the data
    /// in terms of whatever provisioning agreements might be in force (ie, "Q1 2005"
    /// if that is the time of publication for a data set published on a quarterly basis).
    /// </summary>
    public class MetadataSetType : Artefact, ISdmxDelChild, ISdmxNewChild
    {
        /// <summary>
        /// Used to allows loading different MetadaSets that are not identified via
        /// annotation of type 'setID'
        /// </summary>
        static int undefinedNr = 0;

        #region Properties

        // Set id is only in sdmx 2.0, so uses Annotation to hold it in 2.0
        // Agency ID and version are takes as similar to refered MSD
        // Reference to MSD
        public ArtefactRefTyped MetadataStructureRef { get; set; }

        public string ReportingBeginDate { get; set; }

        public string ReportingEndDate { get; set; }

        public string PublicationYear { get; set; }

        public string PublicationPeriod { get; set; }

        private string _reportRefId;
        public string ReportRef
        {
            get { return _reportRefId; }
            set { _reportRefId = value; }
        }


        private AttributeValueSetTypeList _attributeValueSetList;
        public AttributeValueSetTypeList AttributeValueSetList
        {
            get { return _attributeValueSetList; }
        }


        #endregion //Properties

        #region Refered Artefacts

        public override ArtefactRefTypedList ArtefactRefList
        {
            get
            {
                ArtefactRefTypedList list = new ArtefactRefTypedList();
                ArtefactRefTyped at = new ArtefactRefTyped(
                    SdmxMl.Manager.SdmxArtefactType.MetadataStructureDefinitions, this.MetadataStructureRef.FullIdent);
                if (list.Contains(at.Type, at.FullIdent) == false)
                    list.Add(at);

                // To scan URI in Attributevalueset list we need to know MSD Report meta info 
                // so not our job

                return list;
            }
        }

        #endregion

        #region Constructors

        private void Init()
        {
            Name = new MultiLang();
            AnnotationList = new AnnotationList();
            MetadataStructureRef = new
                ArtefactRefTyped(SdmxArtefactType.MetadataStructureDefinitions, "undefined", "undefined", "1.0");
            _attributeValueSetList = new AttributeValueSetTypeList();
        }

        public MetadataSetType()
        {
            Init();
        }

        public MetadataSetType(XmlNode nodeArtefact, XmlNamespaceManager nsmgr)
        {
            Init();

            ReportingBeginDate = Xmlhelper.GetAtt(nodeArtefact, "reportingBeginDate");
            ReportingEndDate = Xmlhelper.GetAtt(nodeArtefact, "reportingEndDate");
            ValidFrom = Xmlhelper.GetAtt(nodeArtefact, "validFromDate");
            ValidTo = Xmlhelper.GetAtt(nodeArtefact, "validToDate");
            PublicationYear = Xmlhelper.GetAtt(nodeArtefact, "publicationYear");
            PublicationPeriod = Xmlhelper.GetAtt(nodeArtefact, "publicationPeriod");

            // For ambiguity between non existing tag in SDMX 2.0 some name it differently
            string versionRef = Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:MetadataStructureVersionRef", nsmgr);
            if (versionRef == string.Empty)
                versionRef = Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:MetadataStructureVersion", nsmgr);

            MetadataStructureRef = new
                ArtefactRefTyped(SdmxArtefactType.MetadataStructureDefinitions,
                    Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:MetadataStructureRef", nsmgr),
                    Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:MetadataStructureAgencyRef", nsmgr),
                    versionRef);

            _reportRefId = Xmlhelper.GetNodeText(nodeArtefact, "./genericmetadata:ReportRef", nsmgr);

            MultiLangNodeReader mr = new MultiLangNodeReader();
            mr.Read(Name, nodeArtefact, "Name", "genericmetadata", nsmgr, false);

            // AttributeValueSet
            XmlNodeList nodeList = nodeArtefact.SelectNodes("./genericmetadata:AttributeValueSet", nsmgr);
            foreach (XmlNode nm in nodeList)
            {
                AttributeValueSetType a = new AttributeValueSetType(nm, nsmgr, this);
                _attributeValueSetList.Add(a);
            }

            nodeList = nodeArtefact.SelectNodes("./genericmetadata:Annotations/common:Annotation", nsmgr);
            foreach (XmlNode na in nodeList)
            {
                Annotation a = new Annotation(na, nsmgr);
                AnnotationList.Add(a);
            }

            // SetID and version given through annotation in SDMX 2.0 
            Annotation anId = AnnotationList.GetAnnotationType("setID");
            if (anId != null && anId.Title != null && anId.Title.Split('+').Length == 3)
            {
                AnnotationList.Remove(anId);
                ArtefactRefTyped art = new ArtefactRefTyped(SdmxArtefactType.MetadataSet, anId.Title);
                Id = art.Id;
                AgencyId = art.AgencyId;
                Version = art.Version;
            }
            else
            {
                Id = AgencyId = "undefined_" + (++undefinedNr).ToString();
                Version = "1.0";
            }
            // IsFinal given through annotation in SDMX 2.0 
            Annotation anFinal = AnnotationList.GetAnnotationType("setIsFinal");
            if (anFinal != null)
            {
                AnnotationList.Remove(anFinal);
                IsFinal = anFinal.Title.ToLower() == "true";
            }

        }


        #endregion // Constructors

        #region Artefact Type

        public override Manager.SdmxArtefactType ArType
        {
            get { return Manager.SdmxArtefactType.MetadataSet; }
        }

        #endregion

        #region XML serialize

        public override void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            Xmlhelper.WriteAttributeIfExist(xw, "reportingBeginDate", ReportingBeginDate);
            Xmlhelper.WriteAttributeIfExist(xw, "reportingEndDate", ReportingEndDate);
            Xmlhelper.WriteAttributeIfExist(xw, "validFromDate", ValidFrom);
            Xmlhelper.WriteAttributeIfExist(xw, "validToDate", ValidTo);
            Xmlhelper.WriteAttributeIfExist(xw, "publicationYear", PublicationYear);
            Xmlhelper.WriteAttributeIfExist(xw, "publicationPeriod", PublicationPeriod);


            // Name
            Name.WriteXml(xw, prefix, "Name");

            // Required
            xw.WriteElementString(prefix, "MetadataStructureRef", null, MetadataStructureRef.Id);
            xw.WriteElementString(prefix, "MetadataStructureAgencyRef", null, MetadataStructureRef.AgencyId);
            if (MetadataStructureRef.Version != "1.0")
                xw.WriteElementString(prefix, "MetadataStructureVersionRef", null, MetadataStructureRef.Version);
            xw.WriteElementString(prefix, "ReportRef", null, _reportRefId);

            // One required.
            if (_attributeValueSetList.Count == 0)
                _attributeValueSetList.Add(new AttributeValueSetType(this));

            foreach (AttributeValueSetType a in _attributeValueSetList)
                a.WriteXml(xw, prefix, "AttributeValueSet");

            // Add isFinal as annotation
            Annotation anFinal = new Annotation("setIsFinal");
            anFinal.Title = IsFinal.ToString();
            anFinal.Text = new MultiLang("Simulate isFinal through annotation");
            AnnotationList.Insert(0, anFinal);

            // Add setID as annotation
            Annotation anId = new Annotation("setID");
            anId.Title = this.FullIdent;
            AnnotationList.Insert(0, anId);

            AnnotationList.WriteXml(xw, prefix, null);
            AnnotationList.Remove(anId);
            AnnotationList.Remove(anFinal);

            xw.WriteEndElement();
        }

        #endregion //XML serialize

        #region Nodes

        public override TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = new TreeNode(this.ToString());
            t.Tag = this;
            parentNode.Nodes.Add(t);

            foreach (AttributeValueSetType at in _attributeValueSetList)
                at.FillNode(t);

            return t;
        }

        #endregion //Nodes

        #region String Representation

        public override string ToString()
        {
            string s = Id + " - " + ReportRef + " - " + Name.ToString();
            if (Version != "1.0")
                s += " - " + Version;
            return s;
        }

        #endregion //String Representation

        #region ISdmxDelChild Members

        public void DelChild(object o)
        {
            AttributeValueSetType a = o as AttributeValueSetType;
            if (a != null)
                _attributeValueSetList.Remove(a);
        }

        #endregion

        #region ISdmxNewChild Members

        public TreeNode NewChild(TreeNode node)
        {
            TreeNode t = null;

            // Are references valid ?
            ArtefactList alMetadataStructureDefinitionsList = null;
            MetadataStructureDefinitionType msd = null;
            ReportStructureType r = null;

            alMetadataStructureDefinitionsList = ReferenceManager.GetArtefactList(SdmxArtefactType.MetadataStructureDefinitions);
            msd = ReferenceManager.LocateMetadataStructureDefinition(this.MetadataStructureRef);
            r = ReferenceManager.LocateReportStructure(this.MetadataStructureRef, ReportRef);

            if (msd != null && r != null)
            {
                AttributeValueSetType at = new AttributeValueSetType(msd, r, this);
                AttributeValueSetList.Add(at);
                at.TargetRef = r.TargetId;
                t = at.FillNode(node);
            }
            return t;
        }

        #endregion

        #region Check Keys Uniqueness

        /// <summary>Check uniqueness of metaset key part </summary>
        /// <returns>True if unique, false otherwise</returns>
        public List<string> CheckKeyUnique()
        {
            List<string> dupliList = new List<string>();
            System.Collections.Generic.Dictionary<string, int> dicokeys = new System.Collections.Generic.Dictionary<string, int>();
            foreach (AttributeValueSetType at in _attributeValueSetList)
            {
                // compose row key
                StringBuilder sb = new StringBuilder();
                foreach (ComponentValueType comp in at.TargetValuesTypeList)
                {
                    sb.Append(comp.Value).Append('+');
                }
                if (dicokeys.ContainsKey(sb.ToString()))
                {
                    dupliList.Add(sb.ToString());
                }
                else
                {
                    dicokeys.Add(sb.ToString(), 0);
                }
            }

            return dupliList;
        }


        #endregion
    }

}

